// This file is a part of Funky C++.
// Copyright (C) 2011, 2013  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "signal/function.hh"
#include "jack/client.hh"
#include <cmath>
#include "signal/sum.hh"
#include "signal/midi_source.hh"
#include <bitset>
#include "signal/filter.hh"
#include "signal/rs.hh"
#include "signal/map_keys.hh"
#include "signal/mix.hh"
#include "signal/discrete.hh"
#include "player.hh"
#include "signal/fft.hh"
#include "signal/long_samples.hh"
#include "signal/complex.hh"
#include "signal/constant.hh"
#include "signal/convolution.hh"
#include <random>
#include <iostream>
#include <chrono>
#include <thread>

int main() {
  using namespace funky;

  jack::client jc{"The thing!"};

  auto long_sin =
    function_signal<float(float)>
    {[](float i) { return (::sin(i/10) + 1.)/2.; }, 2*M_PI*10};
  auto some_sin =
    function_signal<float(float)>
    {[](float i) { return ::sin(i); }, 2*M_PI};
  auto fast_sin =
    function_signal<float(float)>
    {[](float i) { return ::sin(i*2); }, M_PI};
  auto waveform = (some_sin + fast_sin);

  auto midi = midi_source<float>(jc);
  auto synth =
    long_sin *
    mix(map(waveform,
	    rs(filter([](const jack::midi_event& e) {
		  std::bitset<128> ret;
		  if (e.data[0] == 0x90)
		    if (e.data[1] < 128)
		      ret.set(e.data[1]);
		  return ret;
		}, midi),
	      filter([](const jack::midi_event& e) {
		  std::bitset<128> ret;
		  if (e.data[0] == 0x80)
		    if (e.data[1] < 128)
		      ret.set(e.data[1]);
		  return ret;
		}, midi))));

  std::normal_distribution<float> n{0., 0.2};
  std::mt19937 e;

  auto decreasing_noise = function_signal<float(size_t)> {
    [&n, &e] (size_t t) {
      if (t == 0)
	return 1.;
      static auto divisor = log((20.*1024.-1.)/(20.*1024.));
      return .01 * n(e) * exp(double(t) * divisor);
    },
    2u*5u*10240u};

  auto hann = function_signal<float(size_t)>
    {[] (size_t n) {
	return (1. - cos(2. * M_PI * n / 1023.)) / 2.;
      }, 1024u};

  auto fir = buffer<512u, 1024u>::to(make_discrete(synth, .005), hann);
  auto fft = dft(fir);

  auto rectangle = constant<float>(1.);

  // FIXME: There are still discontinuities in this way.
  auto response_fft = dft(buffer<1024u, 1024u>::to(decreasing_noise, rectangle));
  auto conv = convolve<2u*5u*10u>(fft, response_fft);

  auto ufft = dft(conv, true);
  auto ub = unbuffer<512u>::from(ufft);
  auto result = ::funky::real(ub);
  auto c = constant<float>(1./1024./16.);
  auto scaledown = c * result;

  auto p = make_player<512u>(scaledown, jc);
  std::this_thread::sleep_for(std::chrono::seconds(1));
  jc.activate();
  std::this_thread::sleep_for(std::chrono::seconds(100));

  return 0;
}
